Skip to content

NC versions of PreImages, PreImagesSet, PreImagesElm and PreImagesRepresentative - version 2 - #6409

Open
cdwensley wants to merge 4 commits into
masterfrom
preimrep2
Open

NC versions of PreImages, PreImagesSet, PreImagesElm and PreImagesRepresentative - version 2#6409
cdwensley wants to merge 4 commits into
masterfrom
preimrep2

Conversation

@cdwensley

@cdwensley cdwensley commented May 20, 2026

Copy link
Copy Markdown
Contributor

This aims to continue work on the process outlined in issue #4809, and started in PR #5073.
(The latter PR was so far out of sync with the master that rebasing has proved to be very difficult, hence this version.)
The operations PreImages, PreImagesSet, PreImagesElm and PreImagesRepresentative have all been renamed throughout the library by adding 'NC' to their names. New versions of the four operations have been introduced which just add a simple test and then call the NC versions.
Authors of packages which include a method for one of the four operations have been asked to adjust their packages to prepare for the change. The packages which have merged a PR, and made a release after that are: cryst, fr, orb, polycyclic, matgrp, qpa, rcwa, semigroups, utils and wedderga, while fga has merged a PR but not made a release.
The fining package has closed PR#28 without merging it - it installs another method for PreImagesSet.

@codecov

codecov Bot commented May 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 73.90600% with 322 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.97%. Comparing base (66fd147) to head (ea9c680).
⚠️ Report is 8 commits behind head on master.

Files with missing lines Patch % Lines
lib/mapprep.gi 81.27% 41 Missing ⚠️
lib/mapphomo.gi 71.87% 27 Missing ⚠️
lib/ghom.gi 58.13% 18 Missing ⚠️
lib/gprdperm.gi 63.26% 18 Missing ⚠️
lib/mgmring.gi 63.63% 16 Missing ⚠️
lib/alghom.gi 60.00% 14 Missing ⚠️
lib/gprdmat.gi 57.57% 14 Missing ⚠️
lib/relation.gi 80.00% 14 Missing ⚠️
lib/mapping.gi 85.36% 12 Missing ⚠️
lib/ghomperm.gi 80.35% 11 Missing ⚠️
... and 33 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6409      +/-   ##
==========================================
- Coverage   78.97%   78.97%   -0.01%     
==========================================
  Files         684      684              
  Lines      294201   294986     +785     
  Branches     8670     8670              
==========================================
+ Hits       232359   232964     +605     
- Misses      60030    60214     +184     
+ Partials     1812     1808       -4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@stertooy stertooy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very happy to see this PR revived!

One thing I'm a bit wary of, is how PreImagesSet( f, U ) always returns [ ] if U is not a subset of Range( f ), even if they have non-empty intersection. Intuitively, I would expect this function to follow the standard(?) mathematical definition of $f^{-1}(U)$, so something like

PreImagesSetNC( Intersection( Range( f ), U ) )

Comment thread lib/ghomfp.gi Outdated
@cdwensley

Copy link
Copy Markdown
Contributor Author

@stertooy It's so good to get feedback on this work - I will get on with making the changes you suggest.
The way I have done it most of the time, when the new test fails, PreImagesRepresentative returns fail, but PreImagesSet and PreImagesElm (which expect a list) return []. Is that correct, or should this be fail also for PreImagesElm? With PreImagesSet I can see that Intersection( Range(f),U) should be used.

@cdwensley

Copy link
Copy Markdown
Contributor Author

Now I am really confused. The manual states: "If elms is a subset of the range of the general mapping map then PreImagesSet returns the set of all preimages of elms under map." Sticking to that, PreImagesSet should return fail if U is not a subset of the range. Alternatively, we could ditch PreImagesSetNC, and redefine PreImagesSet to return the preimage of the intersection, which could be [].

@cdwensley

cdwensley commented May 21, 2026

Copy link
Copy Markdown
Contributor Author

Perhaps this is why I returned [] in PreImagesElm when the test failed:
Two existing methods for PreImagesElm in mapphomo.gi call PreImagesRepresentative,
and if that returns fail then [] is returned.
I'll do nothing now until advice is received.

Comment thread lib/alghom.gi Outdated
@stertooy

Copy link
Copy Markdown
Contributor

The manual states: "If elms is a subset of the range of the general mapping map then PreImagesSet returns the set of all preimages of elms under map." Sticking to that, PreImagesSet should return fail if U is not a subset of the range.

I think the question is what we want the non-NC functions to do for elements/sets not contained in the range. I see two options:

  1. We limit the scope of these functions to elements/sets contained in the range. The only difference between NC and non-NC functions, is that the latter are guaranteed to return fail if the element/set is not contained in the image.

  2. The non-NC functions behave like the usual mathematical definition of a preimage. So in this case PreImagesElm( f, x ) would return the empty set [ ] if x is not contained in the image of f, and PreImagesSet( f, U ) would return the same as PreImagesSetNC( f, Intersection( Image( f ), U ) ).

The former option keeps the NC and non-NC versions closer together, with the only difference being a extra check, which is what an NC version usually indicates in GAP. The latter option sticks closer to the standard mathematical definitions. Either option is fine with me (with a preference for option 2), but it should at least be consistent across all the affected functions.

Alternatively, we could ditch PreImagesSetNC, and redefine PreImagesSet to return the preimage of the intersection, which could be [].

I think we should still keep PreImagesNC in this case, since certain functions/methods may have prior knowledge of a set actually being contained in the image, and they can then use the NC version to avoid running a superfluous membership check or intersection calculation.

Perhaps @fingolfin @ThomasBreuer @hulpke want to weigh in, given that they were active in #4809 and #5173?

@fingolfin

Copy link
Copy Markdown
Member

Thanks for working on this again, @cdwensley. Unfortunately I don't have the bandwidth available to read all here, think about it, and come up with a good stance right now -- we'll get there, but I am afraid not in time for GAP 4.16.0 -- but I am confident it'll get merged eventually, certainly before 4.17.0.

I'll try to get back to this when I have some mental capacity for it

@hulpke

hulpke commented May 28, 2026

Copy link
Copy Markdown
Contributor

I think part of the reason of NC functions is to actually not have to worry about issues such as membership (which might require an extra test).
I.e. the result of PreImagesSetNC should undefined if the set is not in the image of the map, but if it is returns the same as PreImagesSet.

@cdwensley

cdwensley commented May 28, 2026 via email

Copy link
Copy Markdown
Contributor Author

@cdwensley

cdwensley commented May 28, 2026 via email

Copy link
Copy Markdown
Contributor Author

Comment thread lib/vspchom.gi.changed Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file was added by accident and should be removed again.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@ThomasBreuer ThomasBreuer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up to now, PreImagesSomething( map, obj ) is defined as follows.
If obj is not an element (or subset, respectively) of Range( map ) then anything may happen.
If obj is an element (or subset, respectively) of Range( map ) but not of Image( map ) then fail is returned.
Otherwise, an element (or subset, respectively) of Source( map ) is returned that maps to obj under map.

The idea of the current pull request is to define functions PreImagesSomethingNC with the above definition, and to change the functions PreImagesSomething to signal an error if obj is not an element (or subset, respectively) of Range( map ), and to do the same as PreImagesSomethingNC otherwise.
Thus an error message is guaranteed for PreImagesSomething in the former case, and the user can decide to omit the membership test in Range( map ) by calling PreImagesSomethingNC.
(The text for the release notes would look like that if the pull request gets merged in this form.)

The proposed changes do not address the problems in the example from #4809 and in the second example from #4088, where one asks for the preimage of an element that is in the range but not in the image of the map.
My understanding of the discussion in #4088 was that the membership test for obj in the image of map is what one wants to skip in the new NC variants.
(If this is the case then it will be not difficult to change the pull request accordingly.)

The documentation of the functions should say that fail is returned if obj is not an element (or subset) of the range of map. Currently the proposed text says that the membership is tested.
(An alternative to a fail result would be an error. Of course, if we change the code such that membership in the image of map is tested, then a fail result is correct.)

Comment thread lib/alglie.gi Outdated
Comment thread lib/field.gi Outdated
Comment thread lib/field.gi Outdated
Comment thread lib/liefam.gi Outdated
Comment thread lib/mapping.gd Outdated
Comment thread lib/relation.gi Outdated
if not ( n in Range(rel) ) then
return fail;
fi;
return PreImagesElm( rel, n );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return PreImagesElm( rel, n );
return PreImagesElmNC( rel, n );

Comment thread lib/mapping.gd Outdated
Comment thread lib/mapping.gd Outdated
## From &GAP; version 4.16.0 <C>PreImagesRepresentative</C>
## was renamed <C>PreImagesRepresentativeNC</C> throughout the library,
## and <C>PreImagesRepresentative</C> now checks that <A>elm</A> is an
## element of the range before calling <C>PreImagesRepresentativeNC</C>.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The definition should say what happens if this check fails.

Comment thread lib/mapping.gd
## From &GAP; version 4.16.0 <C>PreImagesSet</C>
## has been renamed <C>PreImagesSetNC</C> throughout the library,
## and <C>PreImagesSet</C> now checks that <A>elms</A> is a
## subset of the range before calling <C>PreImagesSetNC</C>.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The definition should say what happens if this check fails.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many thanks for all these suggestions and making it clear how to proceed.
I have dealt with most of these suggestions, but there is more work to do.
Are we now agreed that, for the non-NC versions:

  • elm/elms/coll not in the range => an Error is thrown, and
  • elm/elms/coll in the range but not the image => fail is returned?
    That being the case, I am adding error messages.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is how I understand the discussions in #4088 and #4809.

The "Error is thrown" part is changed behaviour in the sense that the current documentation promises nothing in this situation, hence the documentation should state that now an error is promised.

The "fail is returned" part is a bugfix in the sense that the documentation promises this but the current code does not follow this rule. (The examples in #4088 show that the current code may return a wrong element or run into an error.)

And the new NC variants allow one to skip the checks that are necessary to achieve these changes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have now added lots of error messages; changed a few [ ] into fail; and added a few more non-NC methods.
The examples in #4088 and #4809 now appear to correctly return fail.

Comment thread lib/mapping.gd Outdated
@cdwensley

Copy link
Copy Markdown
Contributor Author

Advice required about PreImagesSet. If R is the range of map, and if the set U is a subset of R such that the intersection of U and the image of map is V then, mathematically, PreImagesSet(U) is PreImagesSet(V). Is that what should be implemented? At present, fail is returned.

@ThomasBreuer

Copy link
Copy Markdown
Contributor

Advice required about PreImagesSet.

I see, PreImagesSet is conceptually different from PreImagesRepresentative.
(Everything is o.k. with the setup for PreImagesRepresentative: The checks are done by the methods for PreImagesRepresentative, then PreImagesRepresentativeNC gets called which does not do any check.)

The documentation of PreImagesSet says that anything may happen if the given set is not a subset of the Range of the map. Here it makes sense to do the same as for PreImagesRepresentative, that is, make a check and signal an error if the set is not a subset of the range.
For the case that the set is a subset of the Range of the map, we could keep the old definition of PreImagesSet, that is, return the set of all preimages of the set under the map, which may be empty.
The point is that with this definition, PreImagesSetNC must deal with the case that the given set is not necessarily a subset of the Image of the map.
Aren't we then back in the situation before this pull request, that is, that some methods don't do this? (I will have to look at the old code.)

@stertooy

stertooy commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

For the case that the set is a subset of the Range of the map, we could keep the old definition of PreImagesSet, that is, return the set of all preimages of the set under the map, which may be empty.
The point is that with this definition, PreImagesSetNC must deal with the case that the given set is not necessarily a subset of the Image of the map.
Aren't we then back in the situation before this pull request, that is, that some methods don't do this? (I will have to look at the old code.)

Is it not acceptable to have PreImagesSet( f, U ) do something like

if not IsSubset( Range( f ), U ) then
    Error( "..." );
fi;
V := Intersection( Image( f ), U );
return PreImagesSetNC( f, V );

or perhaps, if U is just a list without additional structure,

if not IsSubset( Range( f ), U ) then
    Error( "..." );
fi;
prei := Set( U, u -> PreImagesRepresentative( f, u ) );
RemoveSet( prei, fail );
return prei;

Something like this means we keep the old definition of PreImagesSet, while the NC versions never have to deal with things outside the image.

@ThomasBreuer

Copy link
Copy Markdown
Contributor

Is it not acceptable to have PreImagesSet( f, U ) do something like ...

Proceeding like this means:

  • PreImagesSet( map, C ) signals an error if C is not a subset of Range( map ).
  • Methods for PreImagesSet( map, C ) may compute inter:= Intersection( C, Image( map ) ) and then call PreImagesSetNC( map, inter ).
  • PreImagesSetNC( map, C ) may assume that C is a subset of Image( map ).
  • In my own code, when I know that C is a subset of Image( map ), I should use PreImagesSetNC not PreImagesSet, in order to avoid unnecessary computations.
  • In my own code, when I know that C is a subset of Range( map ), I should compute Intersection( C, Image( map ) ) myself (perhaps in a better way, depending on the situation), and then use PreImagesSetNC.

I think this is acceptable. We make sue that elements not in the image do not cause trouble, and the NC variant allows one to skip expensive tests where this is possible.

(Delegating from PreImagesSet to PreImagesRepresentative for the elements of the given set is not possible because the map need not be injective.)

@cdwensley

Copy link
Copy Markdown
Contributor Author

The proposal from @ThomasBreuer seems very sensible, and I shall proceed to make the suggested changes.

@ThomasBreuer ThomasBreuer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some containment checks for the range should become containment checks for the image.

(I hope that the new tests will not cause infinite loops of the kind that PreImage( map ) calls PreImagesSet( map, Image( map ) ), and Image( map ) calls ImagesSet( map, PreImage( map ) ). However, if that should happens then the question is why this did not cause problems i nthe past.)

Comment thread lib/field.gi Outdated
Comment thread lib/fldabnum.gi Outdated
Comment thread lib/ghom.gi Outdated
Comment thread lib/ghomfp.gi Outdated
Comment thread lib/ghomperm.gi Outdated
Comment thread lib/mapprep.gi Outdated
Comment thread lib/mapprep.gi Outdated
Comment thread lib/mapprep.gi Outdated
Comment thread lib/mapprep.gi Outdated
Comment thread lib/mapprep.gi Outdated
@cdwensley

Copy link
Copy Markdown
Contributor Author

Many thanks @ThomasBreuer for all these corrections - sorry about the silly error.

@ThomasBreuer ThomasBreuer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.
One more suggestion for fixing a typo.
And I am wondering whether also PreImageElm should be changed in the same spirit.

What is missing now is some text for the release notes. The following description would fit but is admittedly quite long.

  • Change the definitions of PreImagesElm, PreImagesRepresentative, PreImagesSet in the case that the given element or set is not contained in the Range of the general mapping:
    Up to now, nothing was guaranteed in this case.
    From now on, an error message is shown in this case.
  • Introduce new operations PreImagesElmNC, PreImagesRepresentativeNC, PreImagesSetNC,
    turn all GAP library methods for PreImagesElm, PreImagesRepresentative, PreImagesSet into methods for the new NC variants, and install methods for the non-NC variants that first test the given element or set for containment in the Range of the given general mapping and then delegate to the NC variants.
  • In all GAP library methods for PreImagesElm, PreImagesRepresentative, PreImagesSet, check whether the given element or set is contained in the Image of the given general mapping.
    Up to now, some of the methods had assumed this and thus gave a wrong answer if the assumption was wrong.
  • Users' code may benefit from changing calls to PreImagesElm, PreImagesRepresentative, PreImagesSet into calls of the NC variants where possible.

Comment thread lib/mapprep.gi Outdated
@cdwensley

Copy link
Copy Markdown
Contributor Author

Regarding PreImageElm - this only applies to bijective maps, and I see no reason to change it.
Far more unsatisfactory are PreImage and PreImages. These two global functions just delegate to one of three operations, so why are they needed? Furthermore the only difference is between PreImage(map,elm) which calls PreImageElm(map,elm) and PreImages(map,elm) which calls PreImagesElm(map,elm) - the other two cases return the same sets.
Cannot remember why, but an NC version of PreImages has been added, but no PreImageNC. Surely it should be neither or both? I'm inclined to remove PreImagesNC. Advice needed!

@cdwensley

Copy link
Copy Markdown
Contributor Author

Interestingly, PreImage is called around 150 times in the main library, but PreImages not at all.

@cdwensley

Copy link
Copy Markdown
Contributor Author

How many of these should be changed? For example, in ghomperm.gi, line 308,
surely G := PreImage( hom );
should be: G := PreImagesRange( hom );
and (more complicated example with fp-groups) in grpfp.gi, line 948,
hom:=MaximalAbelianQuotient(G);
u:=PreImage(hom,TrivialSubgroup(Range(hom)));
could be:
u:=PreImagesSetNC(hom,TrivialSubgroup(Range(hom)));

@ThomasBreuer

Copy link
Copy Markdown
Contributor

Regarding PreImageElm - this only applies to bijective maps, and I see no reason to change it.

The documentation of PreImageElm( map, elm ) says: Anything may happen if elm is not an element of the range of map.
Shouldn't this be changed to a promised error message, for the sake of consistency with the PreImagesSomething operations?
(This need not be part of this pull request.)

Far more unsatisfactory are PreImage and PreImages. These two global functions just delegate to one of three operations, so why are they needed?

The idea was that PreImages( map, obj ) and PreImage( map, obj ) promise an error message if obj is not an element or subset of Range( map ). I do not remember why two functions were introduced.

@cdwensley

Copy link
Copy Markdown
Contributor Author

Thanks for your comments. Away for a few days - will address them when we return.

@fingolfin

Copy link
Copy Markdown
Member

There was a merge conflict in tst/testinstall/ggmbi.tst‎ (looked like a copy&paste accident) which I just resolved (so you should git pull)

@cdwensley

Copy link
Copy Markdown
Contributor Author

As suggested last week I have removed the recently introduced PreImagesNC from the library, though PreImages remains. In fact there were 12 calls to PreImages in the library which were changed to PreImagesNC and are now back to PreImages (in alglie.gi, grp.gi, grpnice.gi, grpprmcs.gi, mapprep.gi and relation.gi). Entries for PreImages in mapping.gd and mapping.gi have reverted to the versions in master.

@ThomasBreuer ThomasBreuer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for the changes, and sorry for not checking them earlier.
I have added a few comments which should be easy to address.

The only missing bit is then the remark for the release notes.
I think that this pull request can be described as follows.

  • Fix the behaviour of the functions in question (in the sense of the examples from #4809).
  • Introduce NC variants for the functions such that the newly introduced checks can be omitted in situations where they are unnecessary.
  • Change the GAP library such that these new NC variants are called. (For GAP package maintainers, we should give a hint that the code of their package may be slowed down by the new checks, and that the recommended solution is to use the NC variants where appropriate.)

Once this is done, we should really merge this pull request.

(And afterwards we should think about follow-up changes for ImagesRepresentativeNC and related functions, since now we know which changes are needed.)

Comment thread lib/mapping.gd Outdated
Comment thread lib/mapping.gd Outdated
Comment thread lib/mapping.gi Outdated
# check that <elm> has at least one preimage under <map>
if IsEmpty( pres ) then
return fail;
return [];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the fail result is correct.
When PreImagesRepresentativeNC was called via a PreImagesRepresentative call then pres cannot be empty, but if one has called PreImagesRepresentativeNC directly then the statement "anything may happen if elm is not an element of the range of map" from the documentation does not cover all cases where elm is not an element of the image of map.

In this sense, we can either change the documentation of PreImagesRepresentativeNC to "anything may happen if elm is not an element of the image of map", or the PreImagesRepresentativeNC methods have to deal with the situation that elm is not in the image.
I prefer the former solution.

Comment thread lib/mapping.gd Outdated
## If <A>elm</A> is not even in the range then an error is signalled.
## <P/>
## When using <C>PreImagesRepresentativeNC</C> anything may happen if
## <A>elm</A> is not an element of the range of <A>map</A>.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## <A>elm</A> is not an element of the range of <A>map</A>.
## <A>elm</A> is not an element of the image of <A>map</A>.

PreImagesRepresentative checks whether elm is in the image, from this point of view PreImagesRepresentativeNC can assume that elm is in the image, and need not deal with situations where this is not the case.
(See the comment about line 1316 of mapping.gi.)

Comment thread lib/mapping.gd
@fingolfin

Copy link
Copy Markdown
Member

Thanks @cdwensley -- I really hope we can finally get this in for GAP 4.17.0

@fingolfin fingolfin added topic: library release notes: highlight PRs introducing changes that should be highlighted at the top of the release notes labels Aug 26, 2026
Comment thread doc/ref/mloop.xml Outdated
Comment thread doc/ref/mloop.xml Outdated
@fingolfin

Copy link
Copy Markdown
Member

@cdwensley I took the liberty of fixing the merge conflicts with master, so if you want to do more work here, you should make sure to first pull from here.

@cdwensley

Copy link
Copy Markdown
Contributor Author

@ThomasBreuer, @fingolfin : thanks for all these comments. Not sure when I shall be able to address them.
I have pulled as Max suggested - I was finding it very difficult to rebase from master.

@fingolfin

Copy link
Copy Markdown
Member

I pushed two more commits (with AI assistance, see the Co-authored-by trailers):

9f504d8 addresses the remaining review comments from @ThomasBreuer:

  • PreImagesRepresentativeNC (for total non-s.p. general mappings) again returns fail instead of [] when the element has no preimage
  • the documentation of the NC variants now says anything may happen if the input is not in the image (rather than: range), matching the checks the non-NC versions perform
  • added the missing <Oper> entry for PreImagesElmNC, so ?PreImagesElmNC works
  • added the suggested explanatory comment to the composition PreImagesElmNC method

While reviewing I also found and fixed a merge accident in lib/mapping.gi: the last master merge had left duplicated PreImagesSet / PreImagesSetNC method installations, one of which implemented outdated semantics (returning [] for elements outside the image, without a range check). The surviving method implements the agreed behaviour. Plus minor cleanup (a stray semicolon, a missing paren in the docs).

91c2c2b adds tst/testinstall/preimages.tst, which systematically exercises the three branches of the new non-NC wrappers. This should also improve the patch coverage codecov complained about.

Not addressed: the suggestion to turn the "membership check + call NC" pattern into a single generic default method for PreImagesRepresentative (comment from July 7). That would remove a fair amount of per-type boilerplate but is a larger restructuring. So I'd leave it for a follow-up PR... But happy to discuss it some more here.

fingolfin added a commit that referenced this pull request Aug 28, 2026
PreImagesElm for field homomorphisms compared IsInjective with 1, a
GAP3 leftover: the injective branch was dead, so every nonzero element
got the empty preimage, even under bijective maps such as Frobenius
automorphisms.

With that branch reachable, PreImagesElm needs PreImagesRepresentative,
which had no applicable method for Frobenius automorphisms.  Add one
that delegates to ImagesRepresentative of the inverse; the inverse of a
Frobenius automorphism is again one (or the identity), so this cannot
recurse.

Note for #6409 (renames these operations to NC variants): the fixed
comparison then belongs in PreImagesElmNC, and the new method in
fieldfin.gi should be installed on PreImagesRepresentativeNC.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@cdwensley

Copy link
Copy Markdown
Contributor Author

I have completely lost touch with what is going on here, but believe that there is not much more to do? I hope someone else can take this PR over?

@fingolfin

Copy link
Copy Markdown
Member

@cdwensley sorry for the chaos, and thank you for shepherding this until here. But I think we are almost done! I'll take this over together with @ThomasBreuer to get it over the finish line soon.

cdwensley and others added 4 commits August 31, 2026 15:26
Add PreImagesElmNC, PreImagesRepresentativeNC and PreImagesSetNC
alongside the existing operations, and document the division of
labour between them: the non-NC versions signal an error if the
given object is not in the range of the mapping, return fail (for
elements) resp. the preimage of its intersection with the image
(for sets) if it is in the range but not in the image, and only
then delegate to the NC version, which performs no checks.

Previously the operations promised a result for objects outside the
image but in fact returned a wrong element or ran into an error.

Co-authored-by: Thomas Breuer <sam@math.rwth-aachen.de>
Co-authored-by: Max Horn <max@quendi.de>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Move every existing method for PreImagesElm, PreImagesRepresentative
and PreImagesSet to the corresponding NC operation, and install new
methods for the non-NC operations which perform the range and image
checks before delegating.  The call sites inside the mapping
machinery itself switch to the NC operations along with this, since
they legitimately ask for preimages of objects outside the image.

The FGA package installs a method for PreImagesRepresentative of a
surjective endomorphism of a free group, which the checked version
can no longer reach; the regression test for issue T00264 therefore
calls the NC variant.

Co-authored-by: Thomas Breuer <sam@math.rwth-aachen.de>
Co-authored-by: Sam Tertooy <5571903+stertooy@users.noreply.github.com>
Co-authored-by: Max Horn <max@quendi.de>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Mechanical change: wherever the library asks for preimages of an
object it has already established to lie in the image, call the NC
operation, so that the newly added checks are not repeated.
Exercise the three branches of the non-NC operations -- object in the
image, in the range but not the image, and outside the range -- for
the main kinds of mapping: perm, pc and fp group homomorphisms,
general mappings by elements, composition, identity, inverse and zero
mappings, mappings by function, action homomorphisms, direct product
embeddings and projections, field automorphisms, vector space
homomorphisms and binary relations.

Co-authored-by: cdwensley <cdwensley.maths@btinternet.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@fingolfin

Copy link
Copy Markdown
Member

OK history squashed into four commits. Which I have not yet reviewed myself, but others, feel free to do so now. I'll also do it later. I hope we can merge this soon :-)

@ThomasBreuer ThomasBreuer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this distribution into commits, this is indeed easier to review.

One comment addresses a range vs. image question, the others are technical.

Comment thread lib/mapping.gd
## <C>PreImagesElm</C> was renamed <C>PreImagesElmNC</C>
## throughout the library, and the new <C>PreImagesElm</C> checks that
## <A>elm</A> is an element of the image before calling <C>PreImagesElmNC</C>.
## If <A>elm</A> is in the range but not in the image then <A>fail</A>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<K>fail</K>?

Comment thread lib/mapping.gd
## was renamed <C>PreImagesRepresentativeNC</C> throughout the library,
## and the new <C>PreImagesRepresentative</C> checks that <A>elm</A> is an
## element of the image before calling <C>PreImagesRepresentativeNC</C>.
## If <A>elm</A> is in the range but not in the image then <A>fail</A>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<K>fail</K>?

Comment thread lib/mapping.gd
## Anything may happen if <A>elm</A> is not an element of the range of
## <A>map</A>.
## If <A>elm</A> is not an element of the image of <A>map</A>
## then an error message is returned.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"an error message is returned" sounds strange

Comment thread lib/mapping.gd
## In the third form it delegates to <Ref Oper="PreImagesSet"/>.
## <P/>
## If the second argument is not an element or a subset of the range of
## If the second argument is not an element or a subset of the image of

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is true for PreImage( map, elm ), since map must be surjective in this case.

PreImage( map, set ) checks whether set is a subset of Range( map ). If yes then it calls PreImagesSet( map, set ), and this admits set not contained in Image( map ).

Comment thread lib/mapping.gi
Error( "no default method for s.p. general mapping" );
end );

InstallMethod( PreImagesRepresentativeNC,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a duplicate method installation.

Comment thread lib/mapping.gi
FamRangeEqFamElm,
[ IsSPGeneralMapping, IsObject ], 0,
function( map, elm )
Error( "no default method for s.p. general mapping" );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now this is another duplicate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release notes: highlight PRs introducing changes that should be highlighted at the top of the release notes topic: library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants